package org.ndx.majick.ui.number;

import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.ndx.majick.properties.Property;
import org.ndx.majick.properties.util.PropertyUtils;
import org.ndx.majick.ui.IPropertyUIMetadata;
import org.ndx.majick.ui.object.ObjectPropertyMetadata;

/**
 * Number property metadata allows simple use in spinners
 * @author Nicolas Delsaux
 *
 */
public class NumberPropertyMetadata extends ObjectPropertyMetadata implements
		IPropertyUIMetadata {
	public static enum Rendering {
		JSPINNER
	};
	/**
	 * Class internal logger. MUST NOT be used anywhere else, for logs readability
	 */
	private static final Logger logger = Logger
			.getLogger(NumberPropertyMetadata.class.getName());

	/**
	 * Maximum value, used as an example in spinners
	 */
	private final Property<Number> max;
	/**
	 * Minimum value, used as an example in spinners
	 */
	private final Property<Number> min;
	/**
	 * Step size, used as an example in spinners. Notice step size has no value by default
	 */
	private final Property<Number> step;
	
	private final Property<Rendering> rendering = PropertyUtils.create(Rendering.class, NumberPropertyMetadata.class+".rendering", support);

	private Property<NumberFormat> viewFormat = PropertyUtils.create(NumberFormat.class, NumberPropertyMetadata.class+".viewFormat", support); 

	/**
	 * Build a NumberPropertyMetadata.
	 * in this case, max and min are set from the available MAX_VALUE and MIN_VALUE constants, if they exist in given class (see {@link #set(Property, String)}).
	 * Furthermore, {@link #viewFormat} is set by using the needed number format instance for the underlying locale.
	 * Unfortunatly, for now, format change is not done according to locale. So be aware that changing locale or format may result in incoherent content.
	 * @param type
	 */
	public NumberPropertyMetadata(Class<? extends Number> type) {
		max = PropertyUtils.create(type, NumberPropertyMetadata.class+".max", support);
		min = PropertyUtils.create(type, NumberPropertyMetadata.class+".min", support);
		step = PropertyUtils.create(type, NumberPropertyMetadata.class+".max", support);
		/*
		 * Now fill the fields
		 */
		set(max, "MAX_VALUE");
		set(min, "MIN_VALUE");
		/* And find the correct view format */
		if(Integer.class.isAssignableFrom(type)) {
			viewFormat.set(NumberFormat.getIntegerInstance(getLocale().get()));
		} else if(Long.class.isAssignableFrom(type)) {
			viewFormat.set(NumberFormat.getIntegerInstance(getLocale().get()));
		} else if(Float.class.isAssignableFrom(type)) {
			viewFormat.set(DecimalFormat.getIntegerInstance(getLocale().get()));
		} else if(Double.class.isAssignableFrom(type)) {
			viewFormat.set(DecimalFormat.getIntegerInstance(getLocale().get()));
		}
	}

	/**
	 * Set the property to the given name field in the class, if it exists.
	 * This method allows easy binding of max to MAX_VALUE (min to MIN_VALUE) of standard numerical types and their derived types
	 * @param property
	 * @param fieldName a field name in class
	 */
	private <Type extends Number> void set(Property<Type> property, String fieldName) {
		Class<?> type = property.getType();
		try {
			Field f = type.getDeclaredField(fieldName);
			Object fieldValue = f.get(null);
			property.set((Type) property.getType().cast(fieldValue));
		} catch (SecurityException e) {
			if (logger.isLoggable(Level.FINE))
				logger.log(Level.FINE, "security manager prevents us from using some basic reflection features over Number types", e);
		} catch (NoSuchFieldException e) {
			if (logger.isLoggable(Level.FINE))
				logger.log(Level.FINE, "field "+fieldName+" does not exists", e);
		} catch (IllegalArgumentException e) {
			if (logger.isLoggable(Level.FINE))
				logger.log(Level.FINE, "what ? field "+fieldName+" is not static ?", e);
		} catch (IllegalAccessException e) {
			if (logger.isLoggable(Level.FINE))
				logger.log(Level.FINE, "field "+fieldName+" can't be accessed", e);
		}
		
	}

	public Property<Number> getMax() {
		return max;
	}

	public Property<Number> getMin() {
		return min;
	}

	public Property<Number> getStep() {
		return step;
	}

	public Property<Rendering> getRendering() {
		return rendering;
	}

	public Property<NumberFormat> getViewFormat() {
		return viewFormat;
	}
}
